using System;
using System.Collections.Generic;

namespace Examples.UnitTestATM
{
    public class Account
    {
        private Money _availableBalance;
        private readonly IList<WithdrawMatcher> _withdraws = new List<WithdrawMatcher>
        {
            WithdrawMatcher.Complete,
            WithdrawMatcher.NoFunds,
            WithdrawMatcher.NotEnoughFunds,
        };

        public Account(string accountNumber, int availableBalance) : this(accountNumber, new Money(availableBalance)){}

        public Account(string accountNumber, Money availableBalance)
        {
            AccountNumber = accountNumber;
            _availableBalance = availableBalance;
        }

        public string AccountNumber { get; private set; }

        public WithdrawStatus Withdraw(Money amount)
        {
            WithdrawStatus withdrawStatus = _withdraws.Find(matcher => matcher.IsMatch(amount, _availableBalance)).Do(amount, _availableBalance);
            _availableBalance -= withdrawStatus.Amount;
            return withdrawStatus;
        }
    }

    public class WithdrawMatcher
    {
        public static WithdrawMatcher Complete = new WithdrawMatcher((amt, availableBalance) => availableBalance > amt, (amt, availableBalance) => WithdrawStatus.Complete(amt));
        public static WithdrawMatcher NoFunds = new WithdrawMatcher((amt, availableBalance) => availableBalance <= 0, (amt, availableBalance) => WithdrawStatus.NoFundsAvailable(new Money(0)));
        public static WithdrawMatcher NotEnoughFunds = new WithdrawMatcher((amt, availableBalance) => availableBalance - amt <= 0, (amt, availableBalance) => WithdrawStatus.InsufficentFundsToComplete(availableBalance));
        
        private readonly Func<Money, Money, bool> _matcher;
        private readonly Func<Money, Money, WithdrawStatus> _action;

        public WithdrawMatcher(Func<Money, Money, bool> matcher, Func<Money, Money, WithdrawStatus> action)
        {
            _matcher = matcher;
            _action = action;
        }
        public bool IsMatch(Money amount, Money availableBalance)
        {
            return _matcher(amount, availableBalance);
        }
        public WithdrawStatus Do(Money amount, Money availableBalance)
        {
            return _action(amount, availableBalance);
        }
    }
}